namespace BomberGame
{
    using System;
    using System.Collections.Generic;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Media;

    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Game
    {
        private readonly GraphicsDeviceManager _graphics;

        private readonly Profile _player1;

        private readonly Profile _player2;
        
        private SpriteBatch _spriteBatch;

        private Stage _stage;

        private bool _paused;

        private KeyboardState _previousState;

        private Song _gameplayMusic;

        private uint _width = 15;

        private uint _height = 13;

        private float _endOfRound;

        public Game1(Profile player1, Profile player2)
        {
            this._endOfRound = 0;
            this._paused = false;
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            _player1 = player1;
            _player2 = player2;
            this.Window.Title = "BomberJoe";
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            _spriteBatch = new SpriteBatch(GraphicsDevice);
            
            // Load textures
            Dictionary<string, Texture2D> textures = new Dictionary<string, Texture2D>();
            textures["player1"] = Content.Load<Texture2D>("tex/p1");
            textures["player2"] = Content.Load<Texture2D>("tex/p2");
            textures["joe"] = Content.Load<Texture2D>("tex/the_joe_mod");
            textures["block"] = Content.Load<Texture2D>("tex/block");
            textures["floor"] = Content.Load<Texture2D>("tex/floor");
            textures["wall"] = Content.Load<Texture2D>("tex/wall");
            textures["bomb"] = Content.Load<Texture2D>("tex/bomb");
            textures["fire"] = Content.Load<Texture2D>("tex/fire");
            textures["fire cross"] = Content.Load<Texture2D>("tex/fire_cross_texture");
            textures["fire end"] = Content.Load<Texture2D>("tex/fire_end");
            textures["extra bomb"] = Content.Load<Texture2D>("tex/powerup_bomb");
            textures["extra speed"] = Content.Load<Texture2D>("tex/powerup_speed");
            textures["extra range"] = Content.Load<Texture2D>("tex/powerup_range");

            // Load sound effects.
            Bomb.ExplosionSFX = Content.Load<SoundEffect>("sfx/explosion");
            Fire.FireSFX = Content.Load<SoundEffect>("sfx/fire");
            Player.DieSFX = Content.Load<SoundEffect>("sfx/die");
            PowerUp.PowerUpSFX = Content.Load<SoundEffect>("sfx/powerup");

            // Build a new stage, and fit the game resolution to the stage size.
            _stage = new Stage(_width, _height, textures,  _player1, _player2);
            _graphics.PreferredBackBufferHeight = (int)_height * GameObject.Scale;
            _graphics.PreferredBackBufferWidth = (int)_width * GameObject.Scale;
            _graphics.ApplyChanges();
            
            _previousState = Keyboard.GetState();

            _gameplayMusic = Content.Load<Song>("bgm");

            // Start the music right away
            PlayMusic(_gameplayMusic);
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            Content.Unload();
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Exit game if user press Escape
            // Pause game if user press P
            if (Keyboard.GetState().IsKeyDown(Keys.Escape))
            {
                Exit();
            }

            if (Keyboard.GetState().IsKeyDown(Keys.M) && !_previousState.IsKeyDown(Keys.M))
            {
                MediaPlayer.IsMuted = !MediaPlayer.IsMuted;
            }
            else if (Keyboard.GetState().IsKeyDown(Keys.P) && !_previousState.IsKeyDown(Keys.P))
            {
                _paused = !_paused;
            }

            // Update the stage if the game is unpaused.
            if (!_paused)
            {
                _stage.Update(gameTime);
                
                // If the game is over countdown before exiting to the menu.
                if (_stage.IsGameover)
                {
                    _endOfRound += gameTime.ElapsedGameTime.Milliseconds;
                    if (_endOfRound > 3000)
                    {
                        System.Windows.Forms.MessageBox.Show("Game Over");
                        Exit();
                    }
                }
            }

            _previousState = Keyboard.GetState();
            base.Update(gameTime);
        }

        protected override void OnExiting(object sender, EventArgs args)
        {
            MediaPlayer.Stop();
            base.OnExiting(sender, args);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);
            _spriteBatch.Begin();
            _stage.Draw(_spriteBatch);
            _spriteBatch.End();
            base.Draw(gameTime);
        }

        private static void PlayMusic(Song song)
        {
            // Due to the way the MediaPlayer plays music,
            // we have to catch the exception. Music will play when the game is not tethered
            try
            {
                // Play the music
                MediaPlayer.Play(song);

                // Loop the currently playing song
                MediaPlayer.IsRepeating = true;
            }
            catch (InvalidOperationException)
            {
                Console.WriteLine("An error occurred when trying to play song.");
            }
        }
    }
}
